package com.terrynoya.fight.evaluation
{
	import com.terrynoya.coreLib.MHashMap;
	import com.terrynoya.fight.emuns.CustomFunctionType;
	import com.terrynoya.fight.emuns.Operator;
	import com.terrynoya.fight.evaluation.token.BinaryOperatorData;
	import com.terrynoya.fight.evaluation.token.CustomFunctionData;
	import com.terrynoya.fight.evaluation.token.NumberData;
	import com.terrynoya.fight.evaluation.token.SymbolData;
	import com.terrynoya.fight.evaluation.token.TextData;
	import com.terrynoya.fight.evaluation.token.UnaryOperatorData;
	import com.terrynoya.fight.evaluation.token.UnknownData;
	import com.terrynoya.fight.stateMachine.controllers.HitDef;
	import com.terrynoya.fight.stateMachine.triggers.Alive;
	import com.terrynoya.fight.stateMachine.triggers.Anim;
	import com.terrynoya.fight.stateMachine.triggers.AnimElem;
	import com.terrynoya.fight.stateMachine.triggers.AnimElemTime;
	import com.terrynoya.fight.stateMachine.triggers.AnimTime;
	import com.terrynoya.fight.stateMachine.triggers.Command;
	import com.terrynoya.fight.stateMachine.triggers.Const;
	import com.terrynoya.fight.stateMachine.triggers.Ctrl;
	import com.terrynoya.fight.stateMachine.triggers.Floor;
	import com.terrynoya.fight.stateMachine.triggers.GetHitVar;
	import com.terrynoya.fight.stateMachine.triggers.HitFall;
	import com.terrynoya.fight.stateMachine.triggers.HitOver;
	import com.terrynoya.fight.stateMachine.triggers.HitShakeOver;
	import com.terrynoya.fight.stateMachine.triggers.IfElse;
	import com.terrynoya.fight.stateMachine.triggers.InGuardDist;
	import com.terrynoya.fight.stateMachine.triggers.MoveContact;
	import com.terrynoya.fight.stateMachine.triggers.MoveHit;
	import com.terrynoya.fight.stateMachine.triggers.NumHelper;
	import com.terrynoya.fight.stateMachine.triggers.NumTarget;
	import com.terrynoya.fight.stateMachine.triggers.P2BodyDist;
	import com.terrynoya.fight.stateMachine.triggers.P2Dist;
	import com.terrynoya.fight.stateMachine.triggers.P2StateType;
	import com.terrynoya.fight.stateMachine.triggers.Physics;
	import com.terrynoya.fight.stateMachine.triggers.Pos;
	import com.terrynoya.fight.stateMachine.triggers.PrevStateNo;
	import com.terrynoya.fight.stateMachine.triggers.ProjHitTime;
	import com.terrynoya.fight.stateMachine.triggers.Random;
	import com.terrynoya.fight.stateMachine.triggers.RoundState;
	import com.terrynoya.fight.stateMachine.triggers.SelfAnimExist;
	import com.terrynoya.fight.stateMachine.triggers.StateNo;
	import com.terrynoya.fight.stateMachine.triggers.StateType;
	import com.terrynoya.fight.stateMachine.triggers.SysVar;
	import com.terrynoya.fight.stateMachine.triggers.Time;
	import com.terrynoya.fight.stateMachine.triggers.Var;
	import com.terrynoya.fight.stateMachine.triggers.Vel;
	import com.terrynoya.fight.stateMachine.triggers.Win;
	
	import org.as3commons.reflect.Type;

	public class Tokenizer
	{
		private var _tokenMap:MHashMap;
		
		private var _numberData:NumberData;
		private var _textData:TextData;
		private var _unknownData:UnknownData;
		
		public function Tokenizer()
		{
			this._textData = new TextData();
			this._numberData = new NumberData();
			this._unknownData = new UnknownData();
			this.buildTokenMap();
		}
		
		private function buildTokenMap():void
		{
			this._tokenMap = new MHashMap(); 
			this._tokenMap.put(CustomFunctionType.Ctrl,this.getCusttomData(CustomFunctionType.Ctrl,Ctrl));
			this._tokenMap.put(CustomFunctionType.Command,this.getCusttomData(CustomFunctionType.Command,Command));
			this._tokenMap.put(CustomFunctionType.StateType,this.getCusttomData(CustomFunctionType.StateType,StateType));
			this._tokenMap.put(CustomFunctionType.StateNo,this.getCusttomData(CustomFunctionType.StateNo,StateNo));
			this._tokenMap.put(CustomFunctionType.RoundState,this.getCusttomData(CustomFunctionType.RoundState,RoundState));
			this._tokenMap.put(CustomFunctionType.Time,this.getCusttomData(CustomFunctionType.Time,Time));
			this._tokenMap.put(CustomFunctionType.Anim,this.getCusttomData(CustomFunctionType.Anim,Anim));
			this._tokenMap.put(CustomFunctionType.AniTime,this.getCusttomData(CustomFunctionType.AniTime,AnimTime));
			this._tokenMap.put(CustomFunctionType.Const,this.getCusttomData(CustomFunctionType.Const,Const));
			this._tokenMap.put(CustomFunctionType.Alive,this.getCusttomData(CustomFunctionType.Alive,Alive));
			this._tokenMap.put(CustomFunctionType.Vel,this.getCusttomData(CustomFunctionType.Vel,Vel));
			this._tokenMap.put(CustomFunctionType.Pos,this.getCusttomData(CustomFunctionType.Pos,Pos));
			this._tokenMap.put(CustomFunctionType.IfElse,this.getCusttomData(CustomFunctionType.IfElse,IfElse));
			this._tokenMap.put(CustomFunctionType.Physics,this.getCusttomData(CustomFunctionType.Physics,Physics));
			this._tokenMap.put(CustomFunctionType.Var,this.getCusttomData(CustomFunctionType.Var,Var));
			this._tokenMap.put(CustomFunctionType.GetHitVar,this.getCusttomData(CustomFunctionType.GetHitVar,GetHitVar));
			this._tokenMap.put(CustomFunctionType.HitShakeOver,this.getCusttomData(CustomFunctionType.HitShakeOver,HitShakeOver));
			this._tokenMap.put(CustomFunctionType.HitOver,this.getCusttomData(CustomFunctionType.HitOver,HitOver));
			this._tokenMap.put(CustomFunctionType.SysVar,this.getCusttomData(CustomFunctionType.SysVar,SysVar));
			this._tokenMap.put(CustomFunctionType.AnimElem,this.getCusttomData(CustomFunctionType.AnimElem,AnimElem));
			this._tokenMap.put(CustomFunctionType.HitFall,this.getCusttomData(CustomFunctionType.HitFall,HitFall));
			this._tokenMap.put(CustomFunctionType.SelfAnimExist,this.getCusttomData(CustomFunctionType.SelfAnimExist,SelfAnimExist));
			this._tokenMap.put(CustomFunctionType.Floor,this.getCusttomData(CustomFunctionType.Floor,Floor));
			this._tokenMap.put(CustomFunctionType.InGuardDist,this.getCusttomData(CustomFunctionType.InGuardDist,InGuardDist));
			this._tokenMap.put(CustomFunctionType.MoveContact,this.getCusttomData(CustomFunctionType.MoveContact,MoveContact));
			this._tokenMap.put(CustomFunctionType.P2BodyDist,this.getCusttomData(CustomFunctionType.P2BodyDist,P2BodyDist));	
			this._tokenMap.put(CustomFunctionType.P2StateType,this.getCusttomData(CustomFunctionType.P2StateType,P2StateType));
			this._tokenMap.put(CustomFunctionType.AnimElemTime,this.getCusttomData(CustomFunctionType.AnimElemTime,AnimElemTime));
			this._tokenMap.put(CustomFunctionType.PrevStateNo,this.getCusttomData(CustomFunctionType.PrevStateNo,PrevStateNo));
			this._tokenMap.put(CustomFunctionType.Win,this.getCusttomData(CustomFunctionType.Win,Win));
			this._tokenMap.put(CustomFunctionType.P2Dist,this.getCusttomData(CustomFunctionType.P2Dist,P2Dist));
			this._tokenMap.put(CustomFunctionType.NumHelper,this.getCusttomData(CustomFunctionType.NumHelper,NumHelper));
			this._tokenMap.put(CustomFunctionType.ProjHitTime,this.getCusttomData(CustomFunctionType.ProjHitTime,ProjHitTime));
			this._tokenMap.put(CustomFunctionType.MoveHit,this.getCusttomData(CustomFunctionType.MoveHit,MoveHit));
			this._tokenMap.put(CustomFunctionType.Random,this.getCusttomData(CustomFunctionType.Random,Random));
			this._tokenMap.put(CustomFunctionType.NumTarget,this.getCusttomData(CustomFunctionType.NumTarget,NumTarget));
			//------------------------------------------------------------------------------------------
			//
			// 操作符
			//
			//------------------------------------------------------------------------------------------
			
			this._tokenMap.put("||",new BinaryOperatorData(Operator.LogicalOr,"||","LogicalOr",0));
			this._tokenMap.put("^^",new BinaryOperatorData(Operator.LogicalXor,"^^","LogicalXOr",1));
			this._tokenMap.put("&&",new BinaryOperatorData(Operator.LogicalAnd,"&&","LogicalAnd",2));
			this._tokenMap.put("|",new BinaryOperatorData(Operator.BinaryOr,"|","BinaryOr",3));
			
			this._tokenMap.put("=",new BinaryOperatorData(Operator.Equals,"=","Equals",6));
			this._tokenMap.put("!=",new BinaryOperatorData(Operator.NotEquals,"!=","NotEquals",6));
			this._tokenMap.put("<",new BinaryOperatorData(Operator.Lesser,"<","Lesser",7));
			this._tokenMap.put("<=",new BinaryOperatorData(Operator.LesserEquals,"<=","LesserEquals",7));
			this._tokenMap.put(">",new BinaryOperatorData(Operator.Greater,">","Greater",7));
			this._tokenMap.put(">=",new BinaryOperatorData(Operator.GreaterEquals,">=","GreaterEquals",7));
			
			this._tokenMap.put("+",new BinaryOperatorData(Operator.Plus,"+","Plus",8));
			this._tokenMap.put("-",new BinaryOperatorData(Operator.Minus,"-","Minus",8));
			this._tokenMap.put("/",new BinaryOperatorData(Operator.Divide,"-","Divide",9));
			this._tokenMap.put("*",new BinaryOperatorData(Operator.Multiply,"*","Multiply",9));
			this._tokenMap.put("%",new BinaryOperatorData(Operator.Modulus,"%","Modulus",9));
			
			this._tokenMap.put("!",new UnaryOperatorData(Operator.LogicalNot,"!","LogicalNot"));
			
			this._tokenMap.put("(",new SymbolData("(","LeftPatern"));
			this._tokenMap.put(")",new SymbolData(")","RightPatern"));
			this._tokenMap.put("[",new SymbolData("[","LeftBracket"));
			this._tokenMap.put("]",new SymbolData("]","RightBracket"));
			this._tokenMap.put(",",new SymbolData(",","Comma"));
		}
		
		private function getCusttomData(name:String,typeClass:Class):CustomFunctionData
		{
			return new CustomFunctionData(name,name,Type.forClass(typeClass));
		}
		
		public function tokenize(input:String):Array
		{
			var index:int = 0;
			var rlt:Array = new Array();
			while(true)
			{
				while(index < input.length && Char.isWhiteSpace(input.charAt(index)))
				{
					index++;
				}
				if(index >= input.length)
				{
					break;
				}
				var token:Token = this.read(input,index);
				if(token == null)
				{
					rlt = [];
					return rlt;
				}
				rlt.push(token);
				index += token.length;
				if(index == input.length)
				{
					break;
				}
				if(index > input.length)
				{
					rlt = [];
					return rlt;
				}
			}
			return rlt;
		}
		
		private function read(input:String,index:int):Token
		{
			var quoteMark:String = '"';
			var startChar:String = input.charAt(index);
			//Read Quoted String
			if(startChar == quoteMark)
			{
				var endQuoteIndex:int = input.indexOf(quoteMark,index + 1);
				var text:String = "";
				if(endQuoteIndex != -1)
				{
					text = input.substr(index,endQuoteIndex + 1 - index);
					return new Token(text,this._textData);
				}
				else
				{
					text = input.substr(index, 1);
					return new Token(text,this._unknownData);
				}
			}
			//Read Identifier
			if(Char.isLetter(startChar) == true)
			{
				var length:int = 1;
				while(index + length < input.length && (Char.isLetterOrDigit(input.charAt(index + length)) || input.charAt(index + length) == "."))
				{
					length ++;
				}
				return makeToken(input,index,length);
			}
			//Read Number
			if(Char.isDigit(startChar) || startChar == ".")
			{
				while(index + length < input.length &&  (Char.isDigit(input.charAt(index + length)) || input.charAt(index + length) == "."))
				{
					length ++;
				}
				text = input.substr(index,length);
				return new Token(text,this._numberData);
			}
			
			//Read Mathematical Symbol
			if(!Char.isLetterOrDigit(startChar))
			{
				var nextIndex:int = index + 1;
				var nextChar:String = input.charAt(nextIndex);
				if(nextIndex < input.length && 
					(Char.isWhiteSpace(nextChar) == false && Char.isLetterOrDigit(nextChar) == false))
				{
					var token:Token = this.makeToken(input,index,2);
					if(token != null && token.data != this._unknownData)
					{
						return token; 
					}
				}
				return this.makeToken(input,index,1);
			}
			
			return null;
		}
		
		private function makeToken(value:String,startIndex:int,length:int):Token
		{
			var txt:String = value.substr(startIndex,length);
			var lower:String = txt.toLocaleLowerCase();
			var data:TokenData = this._unknownData; 
			if(this._tokenMap.containsKey(lower))
			{
				data = this._tokenMap.getValue(lower);
			}
			else
			{
				trace("unknown data:"+txt);
			}
			return new Token(lower,data);
		}
	}
}